package gaml.core;

import gaml.error.GamlException;
import gaml.java.ClassSerializerStore;
import gaml.java.DefaultSerializerStoreFactory;
import gaml.java.NodeObjectBridge;
import gaml.text.DefaultParserStoreFactory;
import gaml.text.DefaultPrinterStoreFactory;
import gaml.text.TextNodeBridge;
import gaml.text.YamlParserStore;
import gaml.text.YamlPrinterStore;
import gaml.text.YamlText;
import gaml.yaml.EmptyNode;
import gaml.yaml.YamlNode;
import gaml.yaml.ahchor.YamlAnchorCodeStore;
import gaml.yaml.ahchor.YamlAnchorObjectStore;


/**
 * java objects serialization-deserialization
 * @author shnireck
 *
 */
public class Gaml {
	GamlConfig config = null;
	
	ClassSerializerStore serializers = null;
	
	YamlParserStore parsers = null;
	YamlPrinterStore printers = null; 
	
	/**
	 * new serializer with default settings and base class serializers collection
	 */
	public Gaml () { assign (GamlConfig.getDefault (), DefaultSerializerStoreFactory.getStore ()); }
	
	/**
	 * new serializer with specific settings and base class serializers collection
	 * @param config serialization-deserialization settings
	 */
	public Gaml (GamlConfig config) { assign (config, DefaultSerializerStoreFactory.getStore ()); }
	
	/**
	 * new serializer with specific settings and serializers collection
	 * @param config serialization settings
	 * @param serializers collection of java object serializers
	 */
	public Gaml (GamlConfig config, ClassSerializerStore serializers) { assign (config, serializers); }
	

	/**
	 * make a yaml text from object of type T
	 * class clazz
	 * @param <T> type of serializing object
	 * @param object java object to serialize
	 * @param clazz class of object
	 * @param sb string builder, to put serialized data into
	 * @return host object
	 * @throws GamlException some serialization error
	 */
	public <T> Gaml serialize (T object, Class <T> clazz, StringBuilder sb) throws GamlException {
		YamlAnchorObjectStore anchors = new YamlAnchorObjectStore ();
		YamlNode node = NodeObjectBridge.create (object, null, clazz, serializers).parseObject (anchors).getNode ();
		anchors.shrinkAnchors();
		
		sb.append (new TextNodeBridge (new YamlText (config, 0), node).parseNode ((null == printers ? DefaultPrinterStoreFactory.getStore (config) : printers)).getText ().getText ());		
		return this;
	}
	
	/**
	 * get data from yaml text and put it into java object
	 * @param <T> type of java object
	 * @param object object, to fill with data 
	 * @param clazz java class of object
	 * @param yaml yaml text with some data
	 * @return host object
	 * @throws GamlException 
	 */
	public <T> Gaml restore (T object, Class <T> clazz, String yaml) throws GamlException {
		YamlAnchorCodeStore anchors = new YamlAnchorCodeStore ();
		YamlNode node = new TextNodeBridge (new YamlText (config, 0).read (yaml), null).parseText ((null == parsers ? DefaultParserStoreFactory.getStore (anchors) : parsers)).getNode ();
		
		NodeObjectBridge.create (node, object, clazz, serializers).parseNode (anchors);
		return this;
	}
	
	/**
	 * get java object - return yaml text as string
	 * @param <T> type of java object
	 * @param object object to serialize
	 * @param clazz java class for java object 
	 * @return serialization yaml text in string representation
	 * @throws GamlException serialization error
	 */
	public <T> String serialize (T object, Class <T> clazz) throws GamlException {
		YamlAnchorObjectStore anchors = new YamlAnchorObjectStore ();
		YamlNode node = NodeObjectBridge.create (object, null, clazz, serializers).parseObject (anchors).getNode ();
		anchors.shrinkAnchors();
		
		return new TextNodeBridge (new YamlText (config, 0), node).parseNode ((null == printers ? DefaultPrinterStoreFactory.getStore (config) : printers)).getText ().getText ();
	}
	
	/**
	 * new java object of class T
	 * data fields serialized from yaml string
	 * @param <T> type of deserialization object
	 * @param yaml serialization yaml text string
	 * @param clazz class of T
	 * @return serialized object of class T
	 * @throws GamlException 
	 */
	public <T> T restore (String yaml, Class <T> clazz) throws GamlException {
		YamlAnchorCodeStore anchors = new YamlAnchorCodeStore ();
		YamlNode node = new TextNodeBridge (new YamlText (config, 0).read (yaml), null).parseText ((null == parsers ? DefaultParserStoreFactory.getStore (anchors) : parsers)).getNode ();
		
		return NodeObjectBridge.create (node, clazz, serializers).parseNode (anchors).getObject ();
	}
	
	/**
	 * serialize java object into yaml text
	 * @param <T> type of serializing object
	 * @param object java object to serialize
	 * @return yaml text string representation
	 * @throws GamlException serialization failure
	 */
	public <T> String serialize (T object) throws GamlException {		
		YamlAnchorObjectStore anchors = new YamlAnchorObjectStore ();
		YamlNode node = (null == object) ? new EmptyNode (null) : NodeObjectBridge.create (object, null, (Class <T>) object.getClass(), serializers).parseObject (anchors).getNode ();
		anchors.shrinkAnchors();
		
		return new TextNodeBridge (new YamlText (config, 0), node).parseNode ((null == printers ? DefaultPrinterStoreFactory.getStore (config) : printers)).getText ().getText ();
	}
	
	/**
	 * access to system collection of class serializers
	 * @return class serializers collection
	 */
	public ClassSerializerStore getSerializers () { return serializers; }
	
	/**
	 * define special yaml node parsers for serialization;
	 * use DefaultParserStoreFactory to get store with basic set of
	 * parsers
	 * @param parsers new yaml node parsers collection 
	 * @return host object
	 */
	public Gaml setParserStore (YamlParserStore parsers) { this.parsers = parsers; return this; }
	
	/**
	 * define special yaml node printers for serialization;
	 * to get initial set of printers use DefaultPrinterStoreFactory
	 * @param printers new collection of yaml node printers
	 * @return host object
	 */
	public Gaml setPrinterStore (YamlPrinterStore printers) { this.printers = printers; return this; }
	
	protected void assign (GamlConfig config, ClassSerializerStore serializers) {
		this.config = config;
		this.serializers = serializers;
	}
}